#include "monitor.h"
#include "temu.h"
#include "watchpoint.h"
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

void cpu_exec(uint32_t);

void display_reg();

/* We use the `readline' library to provide more flexibility to read from stdin. */
// rl_gets 函数的作用是读取用户在终端输入的一行文本，并将其返回，
// 同时将该文本添加到历史记录以供以后查看。这是一个典型的用于创建命令行交互式应用程序的函数
char *rl_gets()
{
	// 声明一个静态指针 line_read，用于存储读取的文本行
	static char *line_read = NULL;

	// 检查 line_read 是否指向已分配的内存块，如果是，则进入下面的条件分支
	if (line_read)
	{
		// 释放之前分配的内存块，这是为了避免内存泄漏，因为每次调用 rl_gets 函数时都会重新分配内存
		free(line_read);
		line_read = NULL;
	}
	// 使用 readline 函数从标准输入读取一行文本，
	// 同时在终端显示提示符 "(temu) "，并将读取的文本分配给 line_read。
	line_read = readline("(temu) ");

	// 检查是否成功读取了文本，以及读取的文本是否不为空
	if (line_read && *line_read)
	{
		// 如果成功读取了文本，将这行文本添加到历史记录，以便用户可以使用上下箭头键查看之前的输入
		add_history(line_read);
	}

	return line_read;
}

// 继续执行程序（执行CPU指令）的命令处理函数
static int cmd_c(char *args)
{
	cpu_exec(-1);
	return 0;
}

// 退出TEMU的命令处理函数
static int cmd_q(char *args)
{
	return -1;
}

// 显示关于支持的命令的信息的命令处理函数
static int cmd_help(char *args);

// 程序单步执行N条指令后暂停，当N没有给出时，缺省为1
static int cmd_si(char *args);

// 打印寄存器状态和监视点信息
static int cmd_info(char *args);

// 扫描内存
static int cmd_x(char *agrs);

// 表达式求值
static int cmd_p(char *args);

// 设置监视点
static int cmd_w(char *args);

// 删除监视点
static int cmd_d(char *args);
static struct
{
	char *name;
	char *description;
	int (*handler)(char *);
} cmd_table[] = {
	{"help", "Display informations about all supported commands", cmd_help},
	{"c", "Continue the execution of the program", cmd_c},
	{"q", "Exit TEMU", cmd_q},
	{"si", "Program pauses after executing N instructions", cmd_si},
	{"info", "Print register status and watchpoint information", cmd_info},
	{"x", "scan memory", cmd_x},
	{"p", "expression evaluation", cmd_p},
	{"w", "setting a watchpoint", cmd_w},
	{"d", "delete a watchpoint", cmd_d}

	/* TODO: Add more commands */

};

// 计算命令表中命令的数量
#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

// 命令 "help" 的实际处理函数。它可以列出所有支持的命令及其描述，或者显示特定命令的描述
static int cmd_help(char *args)
{
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if (arg == NULL)
	{
		/* no argument given */
		for (i = 0; i < NR_CMD; i++)
		{
			printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else
	{
		for (i = 0; i < NR_CMD; i++)
		{
			if (strcmp(arg, cmd_table[i].name) == 0)
			{
				printf("%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		printf("Unknown command '%s'\n", arg);
	}
	return 0;
}

static int cmd_si(char *args)
{
	char *arg = strtok(NULL, " ");
	if (arg == NULL)
	{
		cpu_exec(1);
	}
	else
	{
		int n = atoi(arg);
		cpu_exec(n);
	}
	return 0;
}

static int cmd_info(char *args)
{
	// char *arg = strtok(NULL, " ");
	if (args == NULL)
	{
		printf("please enter args \n");
	}
	else if (0 == strcmp(args, "r"))
	{
		display_reg();
	}
	else if (0 == strcmp(args, "w"))
	{
		WP *head = returnhead();
		WP *temp = head->next;
		while (temp != NULL)
		{
			printf("%d\t%s\t0x%08x\t%d\n", temp->NO, temp->str, temp->expr, temp->expr);
			temp = temp->next;
		}
	}
	else
	{
		printf("Invalid args\n");
	}

	return 0;
}
static int cmd_x(char *agrs)
{
	char *arg1 = strtok(NULL, " ");
	if (arg1 == NULL)
	{
		printf("please enter arg1 \n");
	}
	char *arg2 = strtok(NULL, "\0");
	if (arg2 == NULL)
	{
		printf("please enter arg2 \n");
	}
	int n = atoi(arg1);
	int *success;
	success = (int *)malloc(sizeof(int));
	*success = true;
	uint32_t addr = expr(arg2, success) & 0x7FFFFFFF;
	for (int i = 0; i < n; i++)
	{
		uint32_t result = mem_read(addr, 4);
		// printf("%s\t\t0x%08x\n", arg2, result);
		printf("0x%08x\t", addr);
		for (int i = 24; i >= 0; i -= 8)
		{
			printf("%02x ", ((result >> i) & 0xFF));
		}
		printf("\n");
		addr += 4;
	}
	free(success);
	return 0;
}

static int cmd_p(char *args)
{
	if (args == NULL)
	{
		printf("please enter arg1 \n");
	}
	int *success;
	success = (int *)malloc(sizeof(int));
	*success = true;
	uint32_t result = expr(args, success);
	if (*success == true)
	{
		printf("result =\t0x%08x\t%d\n", result, result);
	}
	free(success);
	return 0;
}

static int cmd_w(char *args)
{
	if (args == NULL)
	{
		printf("please enter arg1 \n");
	}
	int *success;
	success = (int *)malloc(sizeof(int));
	*success = true;
	WP *watchpoint = new_wp();
	strcpy(watchpoint->str, args);
	uint32_t result = expr(args, success);
	watchpoint->expr = result;
	printf("Setting a new watchpoint:\t%d\t%s\t0x%08x\t%d\n", watchpoint->NO, args, result, result);
	free(success);
	return 0;
}

static int cmd_d(char *args)
{
	if (args == NULL)
	{
		printf("please enter arg1 \n");
	}
	int n = atoi(args);
	WP *head = returnhead();
	WP *temp = head->next;
	while (temp->NO != n)
	{
		temp = temp->next;
	}
	printf("Delete a watchpoint:\t%d\t%s\t0x%08x\t%d\n", temp->NO, temp->str, temp->expr, temp->expr);
	free_wp(temp);
	return 0;
}
// 定义了主要的用户界面循环函数。
// 在该函数中，程序将不断等待用户输入命令，解析命令，
// 查找并调用对应的处理函数，或显示错误信息，直到用户选择退出TEMU
void ui_mainloop()
{
	while (1)
	{
		// str字符串就是指令
		char *str = rl_gets();
		// str_end指向指令的结尾
		char *str_end = str + strlen(str);

		/* extract the first token as the command */
		// 从 str 字符串中提取第一个以空格分隔的单词，通常是命令字符串
		char *cmd = strtok(str, " ");
		if (cmd == NULL)
		{
			continue;
		}

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		// 计算 args 指针，该指针指向剩余的字符串，通常是命令的参数
		char *args = cmd + strlen(cmd) + 1;
		if (args >= str_end)
		{
			args = NULL;
		}

		int i;
		for (i = 0; i < NR_CMD; i++)
		{
			// 检查当前提取的命令是否与已注册的命令列表中的某个命令匹配。
			if (strcmp(cmd, cmd_table[i].name) == 0)
			{
				// 调用匹配命令的处理函数，传递参数 args。
				// 如果处理函数返回值小于 0，通常表示退出程序，
				// 此时 ui_mainloop 函数会结束循环，终止程序运行。
				if (cmd_table[i].handler(args) < 0)
				{
					return;
				}
				break;
			}
		}
		// 如果没有找到匹配的命令，将输出 "Unknown command" 消息
		if (i == NR_CMD)
		{
			printf("Unknown command '%s'\n", cmd);
		}
	}
}
